#pragma once

extern CArrayEx<ScriptData*, ScriptData*> Scripts;

struct EventStruct 
{
	DWORD		dwEventId;
	JSFunction* pFunction;
};

class ScriptData
{
	private:
		JSRuntime*	jsRuntime;
		JSContext*	jsCx;
		JSObject*	jsObj;
		JSScript*	jsScript;
		
		BOOL		bStopped;
		BOOL		bInGame;
		
		HANDLE		hThreadHandle;
		DWORD		dwScriptId;

		INT			nIncludeCount;

		CHAR		szFileName[MAX_PATH];
		CHAR		szName[MAX_PATH];
		CHAR		szIncludes[256][256];

		CArrayEx<EventStruct*,EventStruct*> EventArray;

	public:

		ScriptData(JSContext *newContext, JSObject *newObject, CHAR* szNewFile)
		{
			EnterCriticalSection(&Vars.cScriptSection);

			Scripts.Add(this);
			ScriptData::bInGame = TRUE;
			ScriptData::bStopped = FALSE;
			ScriptData::dwScriptId = dwRecentScriptId + 1;
			dwRecentScriptId ++;

			ScriptData::jsRuntime = NULL;
			ScriptData::jsCx = newContext;
			ScriptData::jsObj = newObject;

			memset(szIncludes, NULL, sizeof(szIncludes));
			nIncludeCount = NULL;
		
			memset(szName, NULL, sizeof(szName));
			memset(szFileName, NULL, sizeof(szFileName));
			strcpy(szFileName, szNewFile);

			EventArray.RemoveAll();
			
			LeaveCriticalSection(&Vars.cScriptSection);
		}

		~ScriptData()
		{
			EnterCriticalSection(&Vars.cScriptSection);
			RemoveAllEvents();

			for(INT i = 0; i < Scripts.GetSize(); i++)
			{
				ScriptData* pScript = Scripts.ElementAt(i);

				if(pScript && pScript->GetScriptId() == this->GetScriptId())
				{
					Scripts.RemoveAt(i);
				}
			}

			if(jsCx)
				JS_DestroyContext(jsCx);

			if(jsRuntime)
				JS_Finish(jsRuntime);

			LeaveCriticalSection(&Vars.cScriptSection);
		}

		VOID SetRuntime(JSRuntime* jsR)
		{
			jsRuntime = jsR;
		}

		JSRuntime* GetRuntime(VOID)
		{
			return jsRuntime;
		}

		VOID Stop()
		{
			bStopped = TRUE;
		}

		BOOL isStopped()
		{
			return bStopped;
		}
		
		BOOL CheckFileInclude(CHAR* szFileName)
		{
			for(INT i = 0; i < nIncludeCount; i++)
				if(!strcmp(szFileName, szIncludes[i]))
					return TRUE;

			return FALSE;
		}

		BOOL AddFileInclude(CHAR* szFileName)
		{
			return (BOOL)strcpy(szIncludes[nIncludeCount++], szFileName);
		}

		BOOL SetName(CHAR* szNewName)
		{
			memset(szName, NULL, sizeof(szName));
			return (BOOL)strcpy(szName, szNewName);
		}

		JSContext* GetContext() 
		{
			return jsCx; 
		}

		JSObject* GetObject()
		{
			return jsObj;
		}

		CHAR* GetFile()
		{
			return szFileName;
		}

		CHAR* GetName()
		{
			return szName;
		}

		BOOL RegisterEvent(JSFunction* pFunction, DWORD dwEvent)
		{
			if(!pFunction)
				return FALSE;

			RemoveEvent(dwEvent);

			EventStruct* pEvent = NULL;
			pEvent = new EventStruct;

			if(!pEvent)
				return FALSE;

			pEvent->dwEventId = dwEvent;
			pEvent->pFunction = pFunction;

			EventArray.Add(pEvent);

			return TRUE;
		}

		JSFunction* GetFunction(DWORD dwEvent)
		{
			if(EventArray.IsEmpty())
				return NULL;

			for(INT i = 0; i < EventArray.GetSize(); i++)
			{
				EventStruct* pEvent = EventArray.ElementAt(i);

				if(!pEvent)
					continue;

				if(pEvent->dwEventId == dwEvent)
					return pEvent->pFunction;
			}

			return NULL;
		}

		BOOL RemoveEvent(DWORD dwEvent)
		{
			if(EventArray.IsEmpty())
				return FALSE;

			for(INT i = 0; i < EventArray.GetSize(); i++)
			{
				EventStruct* pEvent = EventArray.ElementAt(i);

				if(!pEvent)
					continue;

				if(pEvent->dwEventId == dwEvent)
				{
					EventArray.RemoveAt(i);
					delete pEvent;

					return TRUE;
				}
			}

			return FALSE;
		}

		VOID RemoveAllEvents(VOID)
		{
			if(EventArray.IsEmpty())
				return;

			for(INT i = 0; i < EventArray.GetSize(); i++)
			{
				EventStruct* pEvent = EventArray.ElementAt(i);

				if(!pEvent)
					continue;

				EventArray.RemoveAt(i);

				delete pEvent;			
			}

			EventArray.RemoveAll();
		}

		BOOL IsInGameScript()
		{
			return bInGame;
		}

		VOID SetInGame(BOOL bValue)
		{
			bInGame = bValue;
		}

		HANDLE GetScriptThreadHandle()
		{
			return hThreadHandle;
		}

		VOID SetScriptThreadHandle(HANDLE hHandle)
		{
			hThreadHandle = hHandle;
		}

		JSScript* GetScript()
		{
			return jsScript;
		}

		VOID SetScript(JSScript* newScript)
		{
			jsScript = newScript;
		}

		DWORD GetScriptId(VOID)
		{
			return dwScriptId;
		}
};